package com.finalproject.endice9x9;

//Stores game model and implements play algorithm (ie die move, push, and game completion)
public class GameModel {
	public static final int ROWS = 9;
	public static final int COLS = 9;
	public static final byte STATEMOVEZERO = 10;
	public static final byte FINALDEST = 1;
	//Stores destinations and states
	//Destinations matrix contains info about final dice destinations 
	byte[][] destinations = new byte[ROWS][COLS];
	//States matrix contains info about dice states
	byte[][] states = new byte[ROWS][COLS];
	//Initialize corresponding matrix from string data
	private boolean initialize(byte[][] dest, String strData) {	
		for (int row = 0; row < ROWS; row++) {
			for (int col = 0; col < COLS; col++) {
				int idxData = row * COLS + col;
				char data = strData.charAt(idxData);
				
				switch(data) {
				case '0':
					dest[row][col] = STATEMOVEZERO;
					break;
					
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
					dest[row][col] = (byte) (data - '0');
					break;
					
				case '.':
					dest[row][col] = 0;
					break;
					
				case 'x':
				case 'X':
					dest[row][col] = FINALDEST;
					break;	
				}					
			}
		}
		return true;
	}
	
	//Initialized game model from string data
	public boolean initialize(String strData) {
		if (strData.length() != ROWS * COLS * 2) {
			return false;
		}
		
		//Initialize destinations
		String strDest = strData.substring(0, ROWS * COLS);
		initialize(destinations, strDest);
		//Initialize states
		String strState = strData.substring(ROWS * COLS);
		initialize(states, strState);
		
		return true;
	}
	
	//Convert game model into string
	public String getModel() {
		String strModel = "";
		// destinations
		for (int row = 0; row < ROWS; row++) {
			for (int col = 0; col < COLS; col++) {		
				byte dest = destinations[row][col];
				if(dest > 0) {
					strModel += "X";
				} else {
					strModel += ".";
				}
			}
		}
		
		// states
		for (int row = 0; row < ROWS; row++) {
			for (int col = 0; col < COLS; col++) {
				byte state = states[row][col];
				if(state > 0) {
					if(state == STATEMOVEZERO) {
						strModel += "0";
					} else {
						strModel += Integer.toString(state);
					}		
				} else {
					strModel += ".";
				}
			}
		}
		
		return strModel;
	}
	
	public byte getDest(int row, int col) {
		return destinations[row][col];
	}
	
	public byte getState(int row, int col) {
		return states[row][col];
	}
	
	//Push dice up using recursive algorithm if possible
	public boolean pushUp(int row, int col) {
		if(states[row][col] == 0) {
			return true;
		}
		if(row > 0) {
			if(pushUp(row-1, col)) {
				states[row-1][col] = states[row][col];
				states[row][col] = 0;
				return true;
			}
		}
		return false;
	}
	
	//Move die up (can push die in front as well)
	public boolean moveUp( int row, int col){
		if(row > 0 && states[row][col] > 0 && states[row][col] != STATEMOVEZERO){
			byte newState = (byte) (states[row][col] - 1);
			if (newState == 0)
				newState = STATEMOVEZERO;
			if(pushUp(row-1, col)) {
				states[row-1][col] = newState;
				states[row][col] = 0;
				return true;
			}
		}
		return false;
	}
	
	//Push dice down using recursive algorithm if possible
	public boolean pushDown(int row, int col) {
		if(states[row][col] == 0) {
			return true;
		}
		if(row < ROWS-1) {
			if(pushDown(row+1, col)){
				states[row+1][col] = states[row][col];
				states[row][col] = 0;
				return true;
			}
		}
		return false;
	}
	
	//Move die down (can push die in front as well)
	public boolean moveDown( int row, int col){
		if(row < ROWS-1 && states[row][col] > 0 && states[row][col] != STATEMOVEZERO) {
			byte newState = (byte) (states[row][col]-1);
			if(newState == 0)
				newState = STATEMOVEZERO;
			if(pushDown(row+1, col)){
				states[row+1][col] = newState;
				states[row][col] = 0;
				return true;
			}
		}
		return false;
	}
	
	//Push dice left using recursive algorithm if possible
	public boolean pushLeft(int row, int col){
		if(states[row][col] == 0) {
			return true;
		}	
		if(col > 0) {
			if(pushLeft(row, col-1)) {
				states[row][col-1] = states[row][col];
				states[row][col] = 0;
				return true;
			}			
		}	
		return false;
	}
	
	//Move die left (can push die in front as well)
	public boolean moveLeft( int row, int col){
		if(col > 0 && states[row][col] > 0 && states[row][col] != STATEMOVEZERO) {
			byte newState = (byte) (states[row][col] - 1);
			if(newState == 0)
				newState = STATEMOVEZERO;
			if(pushLeft(row, col-1)){
				states[row][col-1] = newState;
				states[row][col] = 0;
				return true;
			}
		}
		return false;
	}
	
	//Push dice right using recursive algorithm if possible
	public boolean pushRight(int row, int col) {
		if(states[row][col] == 0) {
			return true;
		}
		if(col < COLS - 1) {
			if(pushRight(row, col+1)) {
				states[row][col+1] = states[row][col];
				states[row][col] = 0;
				return true;
			}
		}
		return false;
	}
	
	//Move die right (can push die in front as well)
	public boolean moveRight(int row, int col){
		if(col < COLS - 1 && states[row][col] > 0 && states[row][col] != STATEMOVEZERO) {
			byte newState = (byte) (states[row][col] - 1);
			if(newState == 0)
				newState = STATEMOVEZERO;
			if(pushRight(row, col+1)) {
				states[row][col+1] = newState;
				states[row][col]= 0;
				return true;
			}
		}
		return false;
	}
	
	//Change tile state to the next one
	public void cycleTile(int row, int col) {
		byte dest = destinations[row][col];
		byte state = states[row][col];
		byte newState = 0;
		byte newDest = 0;
		switch(state) {
		case 0:
			if(dest == 0){
				newDest = 1;
				newState = 0;
			} else {
				newDest = 0;
				newState = 1;
			}
			break;
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
			newState = (byte) (state + 1);
			break;
		case 6:
			newState = STATEMOVEZERO;
			break;
		case STATEMOVEZERO:
			newState = 0;
			newDest = 0;
		}
		destinations[row][col] = newDest;
		states[row][col] = newState;
	}
	
	//Checks if level is completed
	//The level is completed when tiles occupy destinations and can't be moved   
	public boolean isLevelComplete() {
		
		for (int row = 0; row < ROWS; row++) {
			for (int col = 0; col < COLS; col++) {
				if(destinations[row][col] == FINALDEST ) {
					if(states[row][col] != STATEMOVEZERO) {
						return false;
					}
				}
			}
		}
			
		return true;
	}
}
